/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * *
 *            Copyright (C) 2018 Institute of Computing Technology, CAS
 *               Author : Han Shukai (email : hanshukai@ict.ac.cn)
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * *
 *         The kernel's entry, where most of the initialization work is done.
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * *
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this 
 * software and associated documentation files (the "Software"), to deal in the Software 
 * without restriction, including without limitation the rights to use, copy, modify, 
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit 
 * persons to whom the Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE. 
 * 
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * */

#include "fs.h"
#include "irq.h"
#include "test.h"
#include "stdio.h"
#include "sched.h"
#include "screen.h"
#include "common.h"
#include "syscall.h"
#include "smp.h"
#include "mm.h"
#include "mac.h"
#include "time.h"
#include "lock.h"
#include "mailbox.h"

#define TASK_INIT (00)



static void init_memory()
{
    //init_page_table();
    //printk("> [INIT] page table initialization succeeded.\n");
    //init_TLB();
    physical_frame_initial();
}
static void init_lock()
{
    int i;
    for (i = 0; i < 31 ; i++)
    {
        do_mutex_lock_init(&kernel_lock[i]);
    }
}
static void init_pcb()
{
    mbox_init();
    int i = 0;
    for (i = 0; i < NUM_MAX_TASK ; i++)
    {
       pcb[i].status = TASK_EXITED;
       pcb[i].pid = -1;
       pcb[i].which_queue = NULL;
    }
    queue_init(&ready_queue);
    queue_init(&sleep_queue);
    pcb[0].pid = 0;
    pcb[0].status = TASK_RUNNING;
    pcb[0].kernel_state = 1;
    pcb[0].type = KERNEL_PROCESS;
    int queue_ptr = 1;
    
    /*for (i = 0; i < num_timer_tasks; i++, queue_ptr++)
    {
        set_pcb(process_id++, &pcb[queue_ptr], timer_tasks[i]);
        ready_queue_push(&ready_queue, &pcb[queue_ptr]);
    }*/
    /*
    for(i = 0;i < num_sched2_tasks; i++,queue_ptr++)
    {
        set_pcb(process_id++,&pcb[queue_ptr],sched2_tasks[i]);
        ready_queue_push(&ready_queue,&pcb[queue_ptr]);
    }*/
    /*
    for (i = 0; i < num_lock_tasks; i++, queue_ptr++)
    {
        set_pcb(process_id++, &pcb[queue_ptr], lock_tasks[i]);
        ready_queue_push(&ready_queue, &pcb[queue_ptr]);
    }*/

    set_pcb(process_id++, &pcb[queue_ptr], shell_tasks[0]);
    ready_queue_push(&ready_queue, &pcb[queue_ptr]);
    current_running = &pcb[0];
}

static void init_exception_handler()
{
    exception_handler[0] = (uint64_t)handle_int;
    int i = 1;
    for (i = 1; i < 4 ; i++)
        exception_handler[i] = (uint64_t)handle_tlb;
    for ( ; i <32 ; i++)
        exception_handler[i] = (uint64_t)handle_other;
    exception_handler[8] = (uint64_t)handle_syscall;
}

static void init_exception()
{
    
    init_exception_handler();
    /* fill nop */
    /* fill nop */
    memcpy((void *)(BEV0_EBASE + BEV0_OFFSET), exception_handler_entry, exception_handler_end - exception_handler_begin);
    memcpy((void *)BEV0_EBASE, TLBexception_handler_begin, TLBexception_handler_end-TLBexception_handler_begin);
    //memcpy((void *)(BEV1_EBASE + BEV1_OFFSET), exception_handler_entry, exception_handler_end - exception_handler_begin);
    /* set COUNT & set COMPARE */
    reset_timer(TIMER_INTERVAL);
    /* open interrupt */
    
    //initial_cp0_status = 0x00008001;
    //set_cp0_status(initial_cp0_status);
}

// [2]
// extern int read_shell_buff(char *buff);

static void init_syscall(void)
{
        //memset(syscall, 0, sizeof(uint64_t()) * NUM_SYSCALLS);
        syscall[SYSCALL_SPAWN_ARGV] = (uint64_t(*)()) & do_spawn_argv;

        syscall[SYSCALL_MBOX_RECV] = (uint64_t(*)()) & do_mbox_recv;
        syscall[SYSCALL_MBOX_CLOSE] = (uint64_t(*)()) & do_mbox_close;
        syscall[SYSCALL_MBOX_SEND] = (uint64_t(*)()) & do_mbox_send;
        syscall[SYSCALL_MBOX_OPEN] = (uint64_t(*)()) & do_mbox_open; 

        syscall[SYSCALL_CONDITION_INIT] = (uint64_t(*)()) & do_condition_init;
        syscall[SYSCALL_CONDITION_WAIT] = (uint64_t(*)()) & do_condition_wait;
        syscall[SYSCALL_CONDITION_BROADCAST] = (uint64_t(*)()) & do_condition_broadcast;
        syscall[SYSCALL_CONDITION_SIGNAL] = (uint64_t(*)()) & do_condition_signal;
        syscall[SYSCALL_BARRIER_INIT] = (uint64_t(*)()) & do_barrier_init;
        syscall[SYSCALL_BARRIER_WAIT] = (uint64_t(*)()) & do_barrier_wait;
        

        syscall[SYSCALL_SPAWN] = (uint64_t(*)()) & do_spawn;
        syscall[SYSCALL_KILL] = (uint64_t(*)()) & do_kill;
        syscall[SYSCALL_EXIT] = (uint64_t(*)()) & do_exit;
        syscall[SYSCALL_WAITPID] = (uint64_t(*)()) & do_waitpid;
        syscall[SYSCALL_GETPID] = (uint64_t(*)()) & do_getpid;
        syscall[SYSCALL_PS] = (uint64_t(*)()) & do_process_show;

        syscall[SYSCALL_READ_SHELL_BUFF] = (uint64_t(*)()) & read_uart;
        syscall[SYSCALL_BINSEM_GET] = (uint64_t(*)()) & get_lock_id;
        syscall[SYSCALL_BINSEM_OP] = (uint64_t(*)()) & do_op_lock;
        syscall[SYSCALL_GET_TIMER] = (uint64_t(*)()) & get_timer;
        syscall[SYSCALL_YIELD] = (uint64_t(*)()) & do_scheduler;
        syscall[SYSCALL_SCREEN_CLEAR] = (uint64_t(*)()) & screen_clear;
        syscall[SYSCALL_SCREEN_SCROLL] = (uint64_t(*)()) & screen_scroll;
        syscall[SYSCALL_SLEEP] = (uint64_t(*)()) & do_sleep;
        syscall[SYSCALL_WRITE] = (uint64_t(*)()) & screen_write;
        syscall[SYSCALL_CURSOR] = (uint64_t(*)()) & screen_move_cursor;
        syscall[SYSCALL_REFLUSH] = (uint64_t(*)()) & screen_reflush;
        syscall[SYSCALL_MUTEX_LOCK_INIT] = (uint64_t(*)()) & do_mutex_lock_init;
        syscall[SYSCALL_MUTEX_LOCK_ACQUIRE] = (uint64_t(*)()) & do_mutex_lock_acquire;
        syscall[SYSCALL_MUTEX_LOCK_RELEASE] = (uint64_t(*)()) & do_mutex_lock_release;
}

/* [0] The beginning of everything >_< */
void __attribute__((section(".entry_function"))) _start(void)
{

    asm_start();

    
    /* init stack space */
    init_stack();
    printk("> [INIT] Stack heap initialization succeeded.\n");

    /* init interrupt */
    init_exception();
    printk("> [INIT] Interrupt processing initialization succeeded.\n");

    init_memory();
    printk("> [INIT] Virtual memory initialization succeeded.\n");
    // init system call table (0_0)
    /* init system call table */

    init_syscall();
    printk("> [INIT] System call initialized successfully.\n");
    /* init Process Control Block */
    init_pcb();
    printk("> [INIT] PCB initialization succeeded.\n");
    init_lock();
    //printk("> [INIT] kernel locks initialization succeeded.\n");
    /* init screen */
    init_screen();
    printk("> [INIT] SCREEN initialization succeeded.\n");

    /* init filesystem */
    //read_super_block();

    /* wake up core1*/
    //loongson3_boot_secondary();

    /* set cp0_status register to allow interrupt */
    // enable exception and interrupt
    // ERL = 0, EXL = 0, IE = 1
    initial_cp0_status = 0x00008001;
    set_cp0_status(initial_cp0_status);
    while (1)
    {
        //do_scheduler();
    };
    return;
}
